From: Paul Donald Date: Wed, 11 Jun 2025 21:52:01 +0000 (+0200) Subject: file: use lstat for file list (instead of stat) to reveal links X-Git-Url: http://git.openwrt.org/%22https:/collectd.org///%22https:/collectd.org/?a=commitdiff_plain;h=11e0bee504c643e94e54ad6b66185a0164842a5c;p=project%2Frpcd.git file: use lstat for file list (instead of stat) to reveal links I don't think there was a conscious design choice for stat(), but it is documented that the 'type' entry using the rpcd file mechanisms can be 'symlink' but this is never the case using stat(). lstat and stat are virtually identical, but that lstat reports that a symlink entry is a symlink, whereas stat reports that a symlink entry is a file (the file it points to) and info about the link target instead. Someone parsing a directory using stat() would never know about links. Using lstat, we can see whether an entry is a symlink for no extra cost. When the file list encounters a link entry, it now includes the element "target_type" which contains the link target type: "file", "directory" etc - or "broken" if the link is broken - via one additional stat call. So when calling e.g.: ubus call file list '{"path":"/etc"}' instead of: ... "name": "os-release", "type": "file", "size": 610, "mode": 33188, "atime": 1749462226, "mtime": 1749462226, "ctime": 1749462226, "inode": 894, "uid": 0, "gid": 0 ... We get: ... "name": "os-release", "type": "symlink", "size": 21, "mode": 41471, "atime": 1749462226, "mtime": 1749462226, "ctime": 1749462226, "inode": 234, "uid": 0, "gid": 0, "target_type": "file" ... Moved the type determination out of _rpc_file_add_stat to a new function _get_stat_type for re-usability in the if (S_ISLNK(s.st_mode)) block. Tested on: openwrt main Signed-off-by: Paul Donald Tested-by: Eric Fahlgren Link: https://github.com/openwrt/rpcd/pull/13 Signed-off-by: Robert Marko --- diff --git a/file.c b/file.c index f14b274..dc9c88a 100644 --- a/file.c +++ b/file.c @@ -470,8 +470,8 @@ rpc_file_md5(struct ubus_context *ctx, struct ubus_object *obj, return UBUS_STATUS_OK; } -static void -_rpc_file_add_stat(struct stat *s) +static int +_get_stat_type(struct stat *s) { int type; @@ -483,8 +483,13 @@ _rpc_file_add_stat(struct stat *s) S_ISLNK(s->st_mode) ? DT_LNK : S_ISSOCK(s->st_mode) ? DT_SOCK : DT_UNKNOWN; + return type; +} - blobmsg_add_string(&buf, "type", d_types[type]); +static void +_rpc_file_add_stat(struct stat *s) +{ + blobmsg_add_string(&buf, "type", d_types[_get_stat_type(s)]); blobmsg_add_u32(&buf, "size", s->st_size); blobmsg_add_u32(&buf, "mode", s->st_mode); blobmsg_add_u32(&buf, "atime", s->st_atime); @@ -523,11 +528,22 @@ rpc_file_list(struct ubus_context *ctx, struct ubus_object *obj, if (asprintf(&entrypath, "%s/%s", path, e->d_name) < 0) continue; - if (!stat(entrypath, &s)) + // Use lstat to detect symlinks + if (!lstat(entrypath, &s)) { d = blobmsg_open_table(&buf, NULL); blobmsg_add_string(&buf, "name", e->d_name); _rpc_file_add_stat(&s); + + // add target type only for symlinks + if (S_ISLNK(s.st_mode)) { + struct stat target; + if (!stat(entrypath, &target)) { + blobmsg_add_string(&buf, "target_type", d_types[_get_stat_type(&target)]); + } else { + blobmsg_add_string(&buf, "target_type", "broken"); + } + } blobmsg_close_table(&buf, d); }